package com.demo.redis.redis;

import com.alibaba.fastjson.JSON;
import com.demo.redis.entity.User;
import com.demo.redis.redis.annotation.RedisLock;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 注解版redis lock
 * @author cyj
 **/
@Slf4j
@Service
public class RedisLockForAnnotationService {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 测试重复加锁
     * @param userId 用户id
     * @return 返回key有多少个线程加锁
     */
    @RedisLock(key = "#userId")
    public Integer redisLock(String userId){
        log.info("lock success userId:{}", userId);
        return getHoldCount(userId);
    }

    /**
     * 测试获取失败
     * @param userId 用户id
     */
    @RedisLock(key = "#userId", isWaitForLock = false)
    public void redisLockFail(String userId) {
        log.info("打印日志证明获取锁成功，预期是抛异常，userId:{}", userId);
    }

    /**
     * 加锁有过期时间
     * @param userId 用户id
     */
    @SneakyThrows
    @RedisLock(key = "#userId", isWaitForLock = false, expire = 10000)
    public void redisLockNotWaitForLockAndExpire(String userId) {
        log.info("加锁成功, userId:{}", userId);
        Thread.sleep(1000);
    }

    /**
     * list集合使用multilock
     * @param list key集合
     */
    @SneakyThrows
    @RedisLock(key = "#list")
    public Map<String, Integer> redisMultiLockForParamList(List<String> list) {
        log.info("lock success list:{}", list);
        return list.stream().map(s -> {
            RLock lock = redissonClient.getLock(s);
            return ImmutablePair.of(s, lock.getHoldCount());
        }).collect(Collectors.toMap(ImmutablePair::getLeft, ImmutablePair::getRight));
    }

    /**
     * set集合使用multilock
     * @param set key集合
     */
    @SneakyThrows
    @RedisLock(key = "#set")
    public Map<String, Integer> redisMultiLockForParamSet(Set<String> set) {
        log.info("lock success list:{}", set);
        return set.stream().map(s -> {
            RLock lock = redissonClient.getLock(s);
            return ImmutablePair.of(s, lock.getHoldCount());
        }).collect(Collectors.toMap(ImmutablePair::getLeft, ImmutablePair::getRight));
    }

    /**
     * 获取集合对象的属性作为锁
     * spEL表达式说明：key使用了投影表达式的集合投影。![#this.id]是选择Address对象的id属性来构造另一个集合。#this代表集合的每个对象，id表示对象的属性
     * @param user 用户对象
     * @return 返回key对应上锁次数
     */
    @RedisLock(key = "#user.addresses.![#this.id]")
    public Map<Integer, Integer> redisMultiLockForParamListObjectProperty(User user) {
        log.info("lock success user:{}", JSON.toJSONString(user));
        return user.getAddresses().stream().map(s -> {
            RLock lock = redissonClient.getLock(s.getId().toString());
            return ImmutablePair.of(s.getId(), lock.getHoldCount());
        }).collect(Collectors.toMap(ImmutablePair::getLeft, ImmutablePair::getRight));
    }

    /**
     * 测试重复加锁
     * @param userId 用户id
     * @return 返回key有多少个线程加锁
     */
    @RedisLock(key = "#userId")
    @RedisLock(key = "#orderSn")
    public Integer mulitRedisLock(String userId, String orderSn){
        log.info("lock success userId:{}, orderSn:{}", userId, orderSn);

        int userIdHoldCount = getHoldCount(userId);
        log.info("userId:{}，holdCount:{}", userId, userIdHoldCount);

        int orderSnHoldCount = getHoldCount(orderSn);
        log.info("orderSn:{}，holdCount:{}", userId, orderSnHoldCount);
        return userIdHoldCount + orderSnHoldCount;
    }

    private int getHoldCount(String key) {
        RLock lock = redissonClient.getLock(key);
        int holdCount = lock.getHoldCount();
        log.info("holdCount:{}", holdCount);
        return holdCount;
    }
}
